package com.epam.task.web.db;

import com.epam.task.web.app.PropertiesLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Semaphore;

/**
 * User: zhunusbekova.arailym
 */
public class ConnectionPool {
    private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionPool.class);
    private int size;
    private List<Connection> connections = new ArrayList<Connection>();
    private volatile boolean[] checkedOut;
    private Semaphore available;
    private static ConnectionPool instance;
    private Properties connectionProperties;

    private ConnectionPool() {
        try {
            PropertiesLoader propsLoader = new PropertiesLoader(
                    "db.properties");
            connectionProperties = propsLoader.getProps();
            this.size = Integer.valueOf(connectionProperties
                    .getProperty("maxConnections"));
            checkedOut = new boolean[size];
            available = new Semaphore(size, true);
            String url = connectionProperties.getProperty("url");
            String driver = connectionProperties.getProperty("driver");
            String user = connectionProperties.getProperty("user");
            String password = connectionProperties.getProperty("password");
            Class.forName(driver).newInstance();
            for (int i = 0; i < size; ++i) {
                connections.add(DriverManager.getConnection(url, user, password));
            }
        } catch (ClassNotFoundException e) {
            LOGGER.error("Driver was not found! " + e);
        } catch (InstantiationException e) {
            LOGGER.error("",e);
        } catch (IllegalAccessException e) {
            LOGGER.error("",e);
        } catch (Exception e) {
            LOGGER.error("An error occurred! " + e);
        }
    }

    public static ConnectionPool getInstance() {
        if (instance == null) {
            instance = new ConnectionPool();
        }
        return instance;
    }

    public Connection checkOut() throws InterruptedException {
        available.acquire();
        return takeConnection();
    }

    public void checkIn(Connection connection) {
        if (releaseConnection(connection))
            available.release();
    }

    private synchronized Connection takeConnection() {
        for (int i = 0; i < size; ++i)
            if (!checkedOut[i]) {
                checkedOut[i] = true;
                return connections.get(i);
            }
        return null;
    }

    private synchronized boolean releaseConnection(Connection connection) {
        int index = connections.indexOf(connection);
        if (index == -1)
            return false; // Not in the list
        if (checkedOut[index]) {
            checkedOut[index] = false;
            return true;
        }
        return false; // Wasn’t checked out
    }


}
